## 
## File		: df.py 
## 
## Author       : Chris Miles - http://chrismiles.info/
## 
## Start Date	: 20050629
## 
## Description	: Library of classes that deal with Win32 filesystem stats
##
## $Id: df.py 718 2005-08-05 13:41:44Z chris $
##
########################################################################
## (C) Chris Miles 2005
##
## The author accepts no responsibility for the use of this software and
## provides it on an ``as is'' basis without express or implied warranty.
##
## Redistribution and use in source and binary forms are permitted
## provided that this notice is preserved and due credit is given
## to the original author and the contributors.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
########################################################################

"""
  This is an Eddie data collector.  It collects filesystem usage statistics
  using win32file.GetDiskFreeSpace()

  Requires Mark Hammond's win32all package.

  The following statistics are currently collected and made available to
  directives that request it (e.g., FS):

    fs      - Filesystem name (string)
    size    - Size of filesystem (int)
    used    - kB used (int)
    avail   - kB free (int)
    pctused - Percentage Used (int)
    mountpt - Mount point (string) [same as fs on Win32]
"""

# Python Modules
import string
# Win32 Modules
import pywintypes
import win32file
import win32perf
# Eddie Modules
import datacollect
import log


class dfList(datacollect.DataCollect):
    """dfList provides access to disk usage statistics."""

    def __init__(self):
	apply( datacollect.DataCollect.__init__, (self,) )


    ##################################################################
    # Public, thread-safe, methods

    def __str__(self):
        """Create string to display disk usage stats."""

	d = self.getHash()

	rv = ""
        for item in d.keys():
            rv = rv + str(d[item]) + '\n'

        return(rv)


    def __getitem__(self, name):
        """Extends DataCollect.__getitem__() to search mounthash if default
	datahash fails.

	The dfList object can be treated like a dictionary and keyed by
	either device or mount point.
        """

	try:
	    r = apply( datacollect.DataCollect.__getitem__, (self, name) )
	except KeyError:
	    self.data_semaphore.acquire()	# thread-safe access to self.data
	    try:
                r = self.data.mounthash[name]        # try to find mount point
            except KeyError:
		self.data_semaphore.release()
		raise KeyError, "Key %s not found in data hashes" % (name)
	    self.data_semaphore.release()

        return r


    ##################################################################
    # Private methods.  No thread safety if not using public methods.

    def collectData(self):
	"""Collect disk usage data.
	"""

	self.data.datahash = {}
	self.data.mounthash = {}

	drives = win32perf.getDriveNames(filter=(win32perf.DRIVE_FIXED,win32perf.DRIVE_REMOVABLE))
	drives = [d[:-1] for d in drives if d not in ('A:\\', 'B:\\')]	# remove '\'; ignore A: & B:

	for fs in drives:
	    try:
		fsinfo = win32file.GetDiskFreeSpace( fs+'\\' )
	    except pywintypes.error, err:
		log.log( "<df>dfList.collectData(): pywintypes.error, %s"%(err), 7 )
		continue	# skip invalid drives
	    assert len(fsinfo) == 4
	    p = df(fs, fsinfo)
	    self.data.datahash[fs] = p	# dictionary of filesystem devices
	    self.data.mounthash[fs] = p	# dictionary of mount points (hmm, same)

	log.log( "<df>dfList.collectData(): filesystem data collected", 7 )


##################################################################
# Define single filesystem information objects.

class df:
    """df object holds stats on disk usage for a file system."""

    def __init__(self, fsname, fsinfo):
	self.sectors_per_cluster = fsinfo[0]
	self.bytes_per_sector = fsinfo[1]
	self.free_clusters = fsinfo[2]
	self.total_clusters = fsinfo[3]

	self.data = {}
	self.data['fsname']  = fsname			# Filesystem name (device)
	self.data['size']    = self.total_clusters * self.bytes_per_sector * self.sectors_per_cluster / 1024	# Size of filesystem (kBytes)
	self.data['avail']   = self.free_clusters * self.bytes_per_sector * self.sectors_per_cluster / 1024	# kBytes free
	self.data['used']    = self.data['size'] - self.data['avail']	# kBytes used
	self.data['pctused'] = int(100.0 * self.data['used'] / self.data['size'])	# Percentage Used
	self.data['mountpt'] = fsname		# Mount point - same as fsname for Win32


    def __str__(self):
	str = "%-20s %10s %10s %10s %4s %-12s\n" % ("Filesystem","Size","Used","Available","Use%","Mounted on")
	str = str + "%-20s %10s %10s %10s %4s %-12s" % (self.data['fsname'],self.data['size'],self.data['used'],self.data['avail'],self.data['pctused'],self.data['mountpt'])

	return(str)


    def getHash(self):
	"""Return a copy of the filesystem data dictionary.
	"""

	return self.data.copy()


##
## END - df.py
##
